package com.hy.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hy.common.base.RespResult;
import com.hy.common.base.RespResultCode;
import com.hy.common.consts.CommonConst;
import com.hy.common.consts.ExcelConstants;
import com.hy.common.consts.RedisConst;
import com.hy.common.consts.StateConst;
import com.hy.common.excel.DetectDetailExport;
import com.hy.common.exception.BusinessException;
import com.hy.common.utils.CommonUtils;
import com.hy.common.utils.ELUtil;
import com.hy.common.utils.MyObjectUtils;
import com.hy.common.utils.SystemUtils;
import com.hy.entity.dto.*;
import com.hy.entity.pojo.*;
import com.hy.entity.vo.*;
import com.hy.mapper.*;
import com.hy.service.DetectService;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class DetectServiceImpl implements DetectService {
    @Resource
    private DetectDetailExport export;

    @Resource
    private DetectMapper detectMapper;
    @Resource
    private DetectTaskMapper detectTaskMapper;
    @Resource
    private HyPlaceMapper placeMapper;
    @Resource
    private DetectDetailMapper detectDetailMapper;
    @Resource
    private HyPropertyMapper propertyMapper;
    @Resource
    private SqlSessionFactory sqlSessionFactory;
    @Resource
    private RedisTemplate redisTemplate;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult creatTask(DetectAddDTO detectAddDTO) {
        RespResult result=null;
        try{
            HyUserVO currentUser = CommonUtils.getCurrentUser();
            //创建清查单、先检查该单位有没有未清除的盘点单。
            List<Detect> detect = detectMapper.selectList(new LambdaQueryWrapper<Detect>().and(wapper ->
                    wapper.eq(Detect::getUnitId, CommonUtils.getCurrentUser().getUnitId())
            ));
            if (detect.size()!=0){
                //若有未删除的、删除掉。
                detectMapper.deleteBatchIds(detect.stream().map(Detect::getDetectId).collect(Collectors.toList()));
                List<Integer> collect = detectTaskMapper.selectList(new LambdaQueryWrapper<DetectTask>().and(wapper -> {
                    wapper.in(DetectTask::getBelongDetectId, detect.stream().map(Detect::getDetectId).collect(Collectors.toList()));
                })).stream().map(DetectTask::getTaskId).collect(Collectors.toList());
                detectTaskMapper.deleteBatchIds(collect);
            }
            //设置清查单基础信息。插入
            Detect detectDD = detectAddDTO.getDetect();
            detectDD.setCreateDate(CommonUtils.getDate());

            detectDD.setDel(1);
            detectDD.setState(StateConst.DETECT_TASK_ING);
            detectDD.setUnitId(CommonUtils.getCurrentUser().getUnitId());
            detectMapper.insert(detectDD);
            List<DetectDetail> detectDetails=new ArrayList<>();
            //添加清查任务并且添加清查详细
            for (DetectTaskDTO detectTaskDTO : detectAddDTO.getTaskList()) {
                List<String> propertyCodes = (List<String>) redisTemplate.opsForHash().get(RedisConst.DETECT_TASK_PROPERTY_LIST + currentUser.getUserId(), detectTaskDTO.getTaskId());
                DetectTask task=new DetectTask();
                BeanUtils.copyProperties(detectTaskDTO,task);
                task.setPlan(0.00);
                task.setTaskId(null);
                task.setBelongDetectId(detectDD.getDetectId());
                task.setDel(1);
                task.setState(StateConst.DETECT_TASK_ING);
                task.setUnitId(currentUser.getUnitId());
                task.setCreateDate(CommonUtils.getDate());
                detectTaskMapper.insert(task);
                propertyCodes.forEach(e->{
                    HyPropertyVO p= propertyMapper.getPropertyByCode(e);
                    DetectDetail build = DetectDetail.builder()
                            .belongTaskId(task.getTaskId())
                            .placeId(p.getPlaceId())
                            .propertyCode(p.getPropertyCode())
                            .state(StateConst.DETECT_DETAIL_DAICHULI)
                            .gainDate(p.getGainDate())
                            .unitId(p.getUnitId())
                            .propertyName(p.getPropertyName())
                            .propertyTypeId(p.getPropertyTypeId())
                            .upDate(CommonUtils.getDate())
                            .upUserId(CommonUtils.getCurrentUser().getUserId())
                            .userId(p.getOwnerId())
                            .useDeptId(p.getOwnerDeptId())
                            .belongDetectId(detectDD.getDetectId())
                            .build();
                    detectDetails.add(build);

                });
                saveBatch(detectDetails);
                detectDetails.clear();

            }
            result=RespResult.success();
        }catch (Exception e){
            e.printStackTrace();
            result=RespResult.put(RespResultCode.SOCKET_TIMEOUT_EXCEPTION);
        }

        return result;
    }

    @Override
    public RespResult overDetail(String propertyCode,Integer taskId) {
        int update=0;
    try{
        DetectDetail detail = detectDetailMapper.selectOne(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                wapper.eq(DetectDetail::getBelongTaskId, taskId).eq(DetectDetail::getPropertyCode, propertyCode)));
       update = detectDetailMapper.update(null,new LambdaUpdateWrapper<DetectDetail>()
                .eq(DetectDetail::getDetailId,detail.getDetailId())
                .set(DetectDetail::getState,StateConst.DETECT_DETAIL_SURE));
        Long overCount = detectDetailMapper.selectCount(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                wapper.eq(DetectDetail::getBelongTaskId, detail.getBelongTaskId())
                        .ne(DetectDetail::getState, StateConst.DETECT_DETAIL_DAICHULI)

        ));
        Long count = detectDetailMapper.selectCount(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                wapper.eq(DetectDetail::getBelongTaskId, detail.getBelongTaskId())
        ));
        Double plan= overCount.doubleValue()/count.doubleValue();
        DetectTask detectTask = new DetectTask();
        detectTask.setTaskId(taskId);
        detectTask.setPlan(CommonUtils.formatDouble(plan,2));
        detectTaskMapper.updateById(detectTask);
    }
    catch (Exception e){
        e.printStackTrace();
        return RespResult.error(500,"查不到该数据！");
    }

        return update>=0?RespResult.success():RespResult.put(RespResultCode.UPDATE_ERROR);
    }

    @Override
    public RespResult upDetail(DetectUpdateDTO detail) {
        DetectDetail detailReal=new DetectDetail();
        RespResult result=null;
        try {

            BeanUtils.copyProperties(detail,detailReal);
            detailReal.setBelongTaskId(detail.getBelongTaskId());
            detailReal.setBelongDetectId(detail.getBelongDetectId());
            detailReal.setUpUserId(CommonUtils.getCurrentUser().getUserId());
            detailReal.setUpDate(CommonUtils.getDate());
            detailReal.setUnitId(CommonUtils.getCurrentUser().getUnitId());
            detailReal.setPlaceId(MyObjectUtils.isNotBlank(detail.getPlaceId())?detail.getPlaceId():CommonConst.DEFAULT_ID);
            detailReal.setUserId(MyObjectUtils.isNotBlank(detail.getUserId())?detail.getUserId():CommonConst.DEFAULT_ID);
            detailReal.setPropertyTypeId(MyObjectUtils.isNotBlank(detail.getPropertyTypeId())?detail.getPropertyTypeId():CommonConst.DEFAULT_ID);
            detailReal.setUseDeptId(MyObjectUtils.isNotBlank(detail.getUseDeptId())?detail.getUseDeptId():CommonConst.DEFAULT_ID);
            if (detail.getState().equals(StateConst.DETECT_DETAIL_PANYING)){
                DetectDetail detail1 = detectDetailMapper.selectOne(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                        wapper.eq(DetectDetail::getPropertyCode, detailReal.getPropertyCode())
                                .eq(DetectDetail::getBelongDetectId, detailReal.getBelongDetectId())
                ));
                if (MyObjectUtils.isNotBlank(detail1)){
                    detailReal.setDetailId(detail1.getDetailId());
                    result=detectDetailMapper.updateById(detailReal)>0?RespResult.success():RespResult.put(RespResultCode.UPDATE_ERROR);
                }else {
                    detectTaskMapper.addOne(detailReal.getBelongTaskId());
                    result=detectDetailMapper.insert(detailReal)>0?RespResult.put(RespResultCode.Add_SUCCESS):RespResult.put(RespResultCode.ADD_ERROR);

                }
            }else {
                DetectDetail detail01 = detectDetailMapper.selectOne(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                        wapper.eq(DetectDetail::getBelongTaskId, detail.getBelongTaskId()).eq(DetectDetail::getPropertyCode, detail.getPropertyCode())));
                detailReal.setDetailId(MyObjectUtils.isNotBlank(detail01)?detail01.getDetailId():null);
                result=detectDetailMapper.updateById(detailReal)>0?RespResult.success():RespResult.put(RespResultCode.UPDATE_ERROR);
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException("传值有误！");
        }
        Long overCount = detectDetailMapper.selectCount(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                wapper.eq(DetectDetail::getBelongTaskId, detail.getBelongTaskId())
                        .ne(DetectDetail::getState, StateConst.DETECT_DETAIL_DAICHULI)

        ));
        Long count = detectDetailMapper.selectCount(new LambdaQueryWrapper<DetectDetail>().and(wapper ->
                wapper.eq(DetectDetail::getBelongTaskId, detail.getBelongTaskId())
        ));
        Double plan= overCount.doubleValue()/count.doubleValue();
        DetectTask detectTask = new DetectTask();
        detectTask.setTaskId(detail.getBelongTaskId());
        detectTask.setPlan(CommonUtils.formatDouble(plan,2));
        detectTaskMapper.updateById(detectTask);
        return result;
    }

    @Override
    public RespResult detectSearch(DetectTaskSearchDTO detail) {



        Page<Detect> detectTaskPage=detail.getCurrentPage()==null||detail.getPageSize()==null?new Page<>():new Page<>(detail.getCurrentPage(),detail.getPageSize());
        if (CommonUtils.isMember(CommonUtils.getCurrentUser().getRoles(), CommonConst.SYSTEM_ROLE_NAME)){
            detail.setUnitId(null);
        }else {
            detail.setUnitId(CommonUtils.getCurrentUser().getUnitId());
        }
        detail.setDel(1);
        List<DetectVO> tasks= detectMapper.selectByDTO(detectTaskPage,detail);
        return RespResult.success(tasks).setCount(Long.valueOf(detectTaskPage.getTotal()).intValue());
    }

    @Override
    public RespResult detailSearch(DetectDetailSearchDTO detail) {
            List<DetectDetailVO> detectDetailVOS=null;
            Page<DetectDetail> page=ObjectUtils.isEmpty(detail.getCurrentPage())||ObjectUtils.isEmpty(detail.getPageSize())?new Page<>():new Page<>(detail.getCurrentPage(),detail.getPageSize());
        if (CommonUtils.isMember(CommonUtils.getCurrentUser().getRoles(), CommonConst.SYSTEM_ROLE_NAME)){
            detectDetailVOS   =detectDetailMapper.getDetailsByDTO(page,detail);
        }else {
            detail.setUnitId(CommonUtils.getCurrentUser().getUnitId());
            detectDetailVOS   =detectDetailMapper.getDetailsByDTO(page,detail);
        }
        return RespResult.success(detectDetailVOS).setCount(Long.valueOf(page.getTotal()).intValue());
    }

    @Override
    public RespResult sortByPlace() {

        HyUserVO currentUser = CommonUtils.getCurrentUser();
        redisTemplate.delete(RedisConst.TASK_PROPERTY_LIST+currentUser.getUserId());

        //拿到本次盘点的所有的资产。
        List<HyProperty> propertyVOS= propertyMapper.selectList(new LambdaQueryWrapper<HyProperty>().and(wapper->
                            wapper.eq(HyProperty::getUnitId,currentUser.getUnitId())
                ));
        //根据管理人部门来分类、插入Redis。每一个子任务的资产id 、键为部门的ID、值为资产对象数组
        Map<Integer, List<HyProperty>> collect = propertyVOS.stream().collect(Collectors.groupingBy(HyProperty::getPlaceId));
        List<TaskVO> taskVOS=new ArrayList<>();
        for (Map.Entry<Integer, List<HyProperty>> integerListEntry : collect.entrySet()) {
            TaskVO taskVO=new TaskVO();
            taskVO.setTaskId(integerListEntry.getKey());
            HyPlace department = placeMapper.selectById(integerListEntry.getKey());
            taskVO.setTaskName(department.getPlaceName()+"(按照存放区域分类)");
            taskVO.setTaskCount(integerListEntry.getValue().size());
            taskVOS.add(taskVO);
            List<String> propertyCode=new ArrayList<>();
            for (HyProperty hyPropertyVO : integerListEntry.getValue()) {
                propertyCode.add(hyPropertyVO.getPropertyCode());
            }
            redisTemplate.opsForHash().put(RedisConst.DETECT_TASK_PROPERTY_LIST+currentUser.getUserId(),integerListEntry.getKey(),propertyCode);
        }
            redisTemplate.expire(RedisConst.DETECT_TASK_PROPERTY_LIST+currentUser.getUserId(),300, TimeUnit.SECONDS);
            return RespResult.success(taskVOS);
    }
    public    String getSuffix(String name){

        int i = name.lastIndexOf(".");
        return name.substring(i,name.length());


    }
    @Override
    public RespResult uploadPhoto(MultipartFile multipartFile, HttpServletRequest request) {
        RespResult result=null;
        String property1 = System.getProperty("user.dir");
        String name = multipartFile.getOriginalFilename();
        String picName = UUID.randomUUID().toString() +System.currentTimeMillis()+getSuffix(name);
        if(SystemUtils.isWindows()){
            ClassPathResource classPathResource = new ClassPathResource(ExcelConstants.PHOTO_URL_PRE);
            File file= null;
            try {
                file = new File(classPathResource.getFile().getPath()+"/"+picName);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            if (!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            try (InputStream inputStream = multipartFile.getInputStream()) {
                classPathResource = new ClassPathResource(ExcelConstants.PHOTO_URL_PRE+picName);
                FileOutputStream fileOutputStream=new FileOutputStream(classPathResource.getFile());
                int read;
                byte[] b = new byte[1024];
                while ((read = inputStream.read(b))!=-1) {
                    fileOutputStream.write(b);
                }
                fileOutputStream.close();
                fileOutputStream.flush();
                result=RespResult.success(ExcelConstants.PHOTO_URL_PATH+picName);
            } catch (IOException e) {
                result=RespResult.error(500,"上传失败！");
                e.printStackTrace();
            }
        }else if (SystemUtils.isLinux()){
            String property = System.getProperty("user.dir");
            URL path = ClassLoader.getSystemResource("");

            System.out.println("====================================::"+property);
            File file= null;
            try {
                File testFile=new File("/zcsystem"+ExcelConstants.DETECT_PHOTO_URL_PATH);
                if (!testFile.exists()){
                    testFile.mkdir();
                }
                file = new File("/zcsystem"+ExcelConstants.DETECT_PHOTO_URL_PATH+picName);
                if (!file.exists()){
                    file.createNewFile();
                }
            } catch (Exception e) {
                e.printStackTrace();
                return RespResult.error(500,"上传失败！");
            }
            try (InputStream inputStream = multipartFile.getInputStream()) {
                FileOutputStream fileOutputStream=new FileOutputStream(file);
                int read;
                byte[] b = new byte[1024];
                while ((read = inputStream.read(b))!=-1) {
                    fileOutputStream.write(b);
                }
                fileOutputStream.close();
                fileOutputStream.flush();
                result=RespResult.success(ExcelConstants.DETECT_PHOTO_URL_PATH+picName);
            } catch (IOException e) {
                result=RespResult.error(500,"上传失败！");
                e.printStackTrace();
            }
        }



        return result;
    }

    @Override
    public RespResult taskSearch(DetectTaskSearchDTO dto) {
      Page<DetectTask> taskPage=null;
      taskPage= ObjectUtils.isEmpty(dto.getCurrentPage())||ObjectUtils.isEmpty(dto.getPageSize())?new Page<>():new Page<>(dto.getCurrentPage(),dto.getPageSize());
        List<DetectTaskVO> taskVOS=null;

        dto.setDel(1);
        if (CommonUtils.isMember(CommonUtils.getCurrentUser().getRoles(), CommonConst.SYSTEM_ROLE_NAME)){

            taskVOS = detectTaskMapper.getTasksByDTO(taskPage,dto);
        }else if (CommonUtils.isMember(CommonUtils.getCurrentUser().getRoles(),CommonConst.UNIT_ROLE_NAME,CommonConst.PROPERTIES_ROLE_NAME)){
            dto.setUnitId(CommonUtils.getCurrentUser().getUnitId());
            taskVOS = detectTaskMapper.getTasksByDTO(taskPage,dto);
        }else {
            dto.setCheckerId(CommonUtils.getCurrentUser().getUserId());
            dto.setUnitId(CommonUtils.getCurrentUser().getUnitId());
            taskVOS = detectTaskMapper.getTasksByDTO(taskPage,dto);
        }
        return RespResult.success(taskVOS).setCount(Long.valueOf(taskPage.getTotal()).intValue());
    }

    @Override
    public RespResult editDetectDetail(DetectDetail detail) {
        detail.setUpDate(CommonUtils.getDate());
       detectDetailMapper.updateById(detail);
       return RespResult.success();


    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult applyTask(Integer taskId, Integer state) {
        DetectTask task=new DetectTask();
        task.setTaskId(taskId);
        DetectTask detectTask = detectTaskMapper.selectById(taskId);
        RespResult result=null;
        switch (state){
            case 1:
                result=RespResult.put(RespResultCode.PARAM_ERROR);
                break;
            case 2:
                    if (detectTask.getState().equals(StateConst.DETECT_TASK_ING)||detectTask.getState().equals(StateConst.DETECT_TASK_BACK)){
                        task.setState(state);
                        result=RespResult.success();
                        detectTaskMapper.updateById(task);
                    }else {
                        result=RespResult.put(RespResultCode.PARAM_ERROR);
                    }
                    break;
            case 3:
                if (detectTask.getState().equals(StateConst.DETECT_TASK_WAIT_CHECK)){
                    task.setState(state);
                    result=RespResult.success();
                    detectTaskMapper.updateById(task);
                    checkState();
                }else {
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }
                break;
            case 4:
                if (detectTask.getState().equals(StateConst.DETECT_TASK_WAIT_CHECK)){
                    task.setState(state);
                    result=RespResult.success();
                    detectTaskMapper.updateById(task);
                }else {
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }
                break;
            case 5:
                if (detectTask.getState().equals(StateConst.DETECT_TASK_OVER)){
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }else {
                    task.setState(state);
                    result=RespResult.success();
                    detectTaskMapper.updateById(task);
                    checkState();
                }
                break;
            default:
                result=RespResult.error(500,"错误的审核");
        }
        return  result;
    }

    @Override
    public RespResult applyDetect(Integer detectId, Integer state) {
        Detect detect=new Detect();
        detect.setDetectId(detectId);
        Detect detectOne = detectMapper.selectById(detectId);
        RespResult result=null;
        switch (state){
            case 1:
                result=RespResult.put(RespResultCode.PARAM_ERROR);
                break;
            case 2:
                if (detectOne.getState().equals(StateConst.DETECT_TASK_BACK)){
                    detect.setState(state);
                    result=RespResult.success();
                    detectMapper.updateById(detect);
                }else {
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }
            case 3:
            case 4:
                if (detectOne.getState().equals(StateConst.DETECT_TASK_WAIT_CHECK)){
                    detect.setState(state);
                    result=RespResult.success();
                    detectMapper.updateById(detect);
                }else {
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }
                break;
            case 5:
                if (detectOne.getState().equals(StateConst.DETECT_TASK_OVER)){
                    result=RespResult.put(RespResultCode.PARAM_ERROR);
                }else {
                    detect.setState(state);
                    result=RespResult.success();
                    detectMapper.updateById(detect);
                    checkState();
                }
                break;
            default:
                result=RespResult.error(500,"错误的审核");
        }
        return  result;
    }

    @Override
    public RespResult export(HttpServletResponse response) {
        RespResult result=null;
       //导出所有异常的清查任务
        try {
            export.exportWithBigData(response,"清查异常");
            result=RespResult.success();
        }catch (Exception e){
            e.printStackTrace();
            result=RespResult.error(500,"清查单未创建");
        }

        return result;

    }

    public void checkState(){
        DetectTaskSearchDTO dto=new DetectTaskSearchDTO();
        dto.setDel(1);
        boolean flag=true;
        for (DetectTaskVO detectTaskVO : detectTaskMapper.getTasksByDTO(new Page<>(), dto)) {
            if (!detectTaskVO.getState().equals(StateConst.DETECT_TASK_OVER)&&!detectTaskVO.getState().equals(StateConst.DETECT_TASK_STOP)){
                flag=false;
            }
        }
        if (flag){
            List<Detect> detects = detectMapper.selectList(new QueryWrapper<>());
            Detect detect = detects.get(0);
            detect.setState(StateConst.DETECT_TASK_WAIT_CHECK);
            detectMapper.updateById(detect);
        }

    }
    private boolean saveBatch(List<DetectDetail> detectDetails){
        /**
         * mybatis提供三种sql执行器，分别是SIMPLE（默认）、REUSE、BATCH
         * SIMPLE	默认执行器， 节约服务器资源	每次都要开关Statement
         * REUSE	提升后端接口处理效率	每次一个新sql都缓存，增大JVM内存压力
         * BATCH	专门用于更新插入操作，效率最快	对select 不适用，另外特殊要求，比如限制一次execteBatch的数量时需要写过滤器定制
         */
        // 打开批处理
        boolean flag=false;
        try {
            SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
            /*HyPropertyBatchMapper empBatchMapper = session.getMapper(HyPropertyBatchMapper.class);*/
            for (int i = 0,length = detectDetails.size(); i < length; i++) {
                detectDetailMapper.insert(detectDetails.get(i));
//            empBatchMapper.insert(data.get(i));
                //每1000条提交一次防止内存溢出
                if(i % 10000==9999){
                    session.commit();
                    session.clearCache();
                }
            }
            session.commit();
            session.clearCache();
            flag=true;
        }catch (Exception e){
            e.printStackTrace();
            flag=false;
        }

        return flag;
    }
}
